const path = require("path");
const fs = require("fs");
const { resolve } = path;
const { access, constants, existsSync, lstatSync } = fs;
const Ps = require("ee-core/ps");
const electron = require("electron");
const { app, nativeTheme, shell } = electron;
const logger = require("ee-core/log");

const { engineBinMap, engineArchMap } = require("../../config/engine");
// 下载引擎相关

const {
  APP_THEME,
  ENGINE_MAX_CONNECTION_PER_SERVER,
  IP_VERSION,
  IS_PORTABLE,
  PORTABLE_EXECUTABLE_DIR,
} = require("../constants");

const getUserDataPath = () => {
  return IS_PORTABLE ? PORTABLE_EXECUTABLE_DIR : app.getPath("userData");
};

const getSystemLogPath = () => {
  return app.getPath("logs");
};

const getUserDownloadsPath = () => {
  return app.getPath("downloads");
};

const getConfigBasePath = () => {
  const path = getUserDataPath();
  return path;
};

const getSessionPath = () => {
  return resolve(getUserDataPath(), "./download.session");
};

const getEnginePidPath = () => {
  return resolve(getUserDataPath(), "./engine.pid");
};

const getDhtPath = (protocol) => {
  const name = protocol === IP_VERSION.V6 ? "dht6.dat" : "dht.dat";
  return resolve(getUserDataPath(), `./${name}`);
};

const getEngineBin = (platform) => {
  const result = engineBinMap[platform] || "";
  return result;
};

const getEngineArch = (platform, arch) => {
  if (!["darwin", "win32", "linux"].includes(platform)) {
    return "";
  }

  const result = engineArchMap[platform][arch];
  return result;
};

const getDevEnginePath = (platform, arch) => {
  const ah = getEngineArch(platform, arch);
  const base = `../../../build/extraResources/aria2/${platform}/${ah}/engine`;
  const result = resolve(__dirname, base);
  return result;
};

const getProdEnginePath = (platform, arch) => {
  const ah = getEngineArch(platform, arch);
  const appPath = app.isPackaged
    ? path.dirname(app.getPath("exe"))
    : app.getAppPath();
  let epath = app.isPackaged
    ? `./resources/extraResources/aria2/${platform}/${ah}/engine`
    : `./build/extraResources/aria2/${platform}/${ah}/engine`;
  logger.info("getProdEnginePath", appPath, epath, app.isPackaged);
  return resolve(appPath, epath);
};

const getEnginePath = (platform, arch) => {
  return Ps.isDev()
    ? getDevEnginePath(platform, arch)
    : getProdEnginePath(platform, arch);
};

const getAria2BinPath = (platform, arch) => {
  const base = getEnginePath(platform, arch);
  const binName = getEngineBin(platform);
  const result = resolve(base, `./${binName}`);
  return result;
};

const getAria2ConfPath = (platform, arch) => {
  const base = getEnginePath(platform, arch);
  return resolve(base, "./aria2.conf");
};

const transformConfig = (config) => {
  const result = [];
  for (const [k, v] of Object.entries(config)) {
    if (v !== "") {
      result.push(`--${k}=${v}`);
    }
  }
  return result;
};

const isRunningInDmg = () => {
  if (!is.macOS() || is.dev()) {
    return false;
  }
  const appPath = app.getAppPath();
  const result = appPath.startsWith("/Volumes/");
  return result;
};

const moveAppToApplicationsFolder = (errorMsg = "") => {
  return new Promise((resolve, reject) => {
    try {
      const result = app.moveToApplicationsFolder();
      if (result) {
        resolve(result);
      } else {
        reject(new Error(errorMsg));
      }
    } catch (err) {
      reject(err);
    }
  });
};

const splitArgv = (argv) => {
  const args = [];
  const extra = {};
  for (const arg of argv) {
    if (arg.startsWith("--")) {
      const kv = arg.split("=");
      const key = kv[0];
      const value = kv[1] || "1";
      extra[key] = value;
      continue;
    }
    args.push(arg);
  }
  return { args, extra };
};

const parseArgvAsUrl = (argv) => {
  const arg = argv[1];
  if (!arg) {
    return;
  }

  if (checkIsSupportedSchema(arg)) {
    return arg;
  }
};

const checkIsSupportedSchema = (url = "") => {
  const str = url.toLowerCase();
  if (
    str.startsWith("ftp:") ||
    str.startsWith("http:") ||
    str.startsWith("https:") ||
    str.startsWith("magnet:") ||
    str.startsWith("thunder:") ||
    str.startsWith("mo:") ||
    str.startsWith("motrix:")
  ) {
    return true;
  } else {
    return false;
  }
};

const isDirectory = (path) => {
  return existsSync(path) && lstatSync(path).isDirectory();
};

const parseArgvAsFile = (argv) => {
  let arg = argv[1];
  if (!arg || isDirectory(arg)) {
    return;
  }

  if (is.linux()) {
    arg = arg.replace("file://", "");
  }
  return arg;
};

const getMaxConnectionPerServer = () => {
  return ENGINE_MAX_CONNECTION_PER_SERVER;
};

const getSystemTheme = () => {
  let result = APP_THEME.LIGHT;
  result = nativeTheme.shouldUseDarkColors ? APP_THEME.DARK : APP_THEME.LIGHT;
  return result;
};

const convertArrayBufferToBuffer = (arrayBuffer) => {
  const buffer = Buffer.alloc(arrayBuffer.byteLength);
  const view = new Uint8Array(arrayBuffer);
  for (let i = 0; i < buffer.length; ++i) {
    buffer[i] = view[i];
  }
  return buffer;
};

const showItemInFolder = (fullPath) => {
  if (!fullPath) {
    return;
  }

  fullPath = resolve(fullPath);
  access(fullPath, constants.F_OK, (err) => {
    if (err) {
      logger.warn(`[Motrix] ${fullPath} ${err ? "does not exist" : "exists"}`);
      return;
    }

    shell.showItemInFolder(fullPath);
  });
};

module.exports = {
  getUserDataPath,
  getSystemLogPath,
  getUserDownloadsPath,
  getConfigBasePath,
  getSessionPath,
  getEnginePidPath,
  getDhtPath,
  getEngineBin,
  getEngineArch,
  getDevEnginePath,
  getProdEnginePath,
  getEnginePath,
  getAria2BinPath,
  getAria2ConfPath,
  transformConfig,
  isRunningInDmg,
  moveAppToApplicationsFolder,
  splitArgv,
  parseArgvAsUrl,
  checkIsSupportedSchema,
  isDirectory,
  parseArgvAsFile,
  getMaxConnectionPerServer,
  getSystemTheme,
  convertArrayBufferToBuffer,
  showItemInFolder,
};
